#include "parafluidorientation.h"
#include "parafluidsimulation.h"

#include <cstdlib>
#include <ctime>

using namespace ParaFluid;

Simulation::~Simulation()
{
	/* does nothing */
}

Simulation::Simulation(unsigned int buckets)
	: _stepper()
	, _sph(buckets, 1.0, 0.1, Vector(0.0, -0.2, 0.0), Box(Vector(0.0, 0.0, 0.0), Orientation(0.0, 0.0, 0.0), Vector(10.0, 10.0, 10.0)))
	, _substances()
{
	_stepper.setSingleShot(false);
	_stepper.setInterval(10);
	connect(&_stepper, SIGNAL(timeout()), this, SLOT(step()));
	
	_substances.append(Substance(1.0, Color(0.8, 0.2, 0.0), 1.0, 0.1, 1.0));
	_substances.append(Substance(3.0, Color(0.0, 0.1, 0.8), 1.0, 0.1, 1.0));
	
	resetParameters();
}

const SPH & Simulation::sph() const
{
	return _sph;
}

const QVector<Substance> & Simulation::substances() const
{
	return _substances;
}

bool Simulation::isRunning() const
{
	return _stepper.isActive();
}

void Simulation::start()
{
	_stepper.start();
}

void Simulation::pause()
{
	if(_stepper.isActive())
	{
		_stepper.stop();
	}
	else
	{
		_stepper.start();
	}
}

void Simulation::stop()
{
	_stepper.stop();
}

void Simulation::step()
{
	_sph.step();
	emit stepped();
}

void Simulation::resetSimulation()
{
	stop();
	_sph.clear();
	step();
}

void Simulation::resetParameters()
{
	setSmoothingDistance(1.0);
	setTimeStep(0.1);
	setGravity(Vector(0.0, -0.2, 0.0));
	setConstraints(Box(Vector(0.0, 0.0, 0.0), Orientation(0.0, 0.0, 0.0), Vector(10.0, 10.0, 10.0)));
	
	setSubstanceMass(0, 1.0);
	setSubstanceColor(0, Color(0.8, 0.2, 0.0));
	setSubstancePressureFactor(0, 1.0);
	setSubstanceRestDensity(0, 0.1);
	setSubstanceViscosityFactor(0, 1.0);
	
	setSubstanceMass(1, 3.0);
	setSubstanceColor(1, Color(0.0, 0.1, 0.8));
	setSubstancePressureFactor(1, 1.0);
	setSubstanceRestDensity(1, 0.1);
	setSubstanceViscosityFactor(1, 1.0);
}

void Simulation::insertAtoms(int substanceIndex, int qty)
{
	// Initialize Random Seed
	static bool needSeed = true;
	if(needSeed)
	{
		needSeed = false;
		std::srand(std::time(0));
	}
	
	// Insert Random Atoms
	Vector random;
	while(qty > 0)
	{
		random.x = _sph.constraints().position.x + (_sph.constraints().extent.x * float(rand()) / float(RAND_MAX));
		random.y = _sph.constraints().position.y + (_sph.constraints().extent.y * float(rand()) / float(RAND_MAX));
		random.z = _sph.constraints().position.z + (_sph.constraints().extent.z * float(rand()) / float(RAND_MAX));
		_sph.atomVoxelizer.insert(Atom(random, substance(substanceIndex)));
		qty--;
	}
	emit stepped();
}

void Simulation::setSmoothingDistance(float value)
{
	_sph.setSmoothingDistance(value);
	emit changedSmoothingDistance(_sph.smoothingDistance());
}

void Simulation::setTimeStep(float value)
{
	_sph.setTimeStep(value);
	emit changedTimeStep(_sph.timeStep());
}

void Simulation::setGravity(const Vector & value)
{
	_sph.setGravity(value);
	emit changedGravity(_sph.gravity());
}

void Simulation::setConstraints(const Box & value)
{
	_sph.setConstraints(value);
	emit changedConstraints(_sph.constraints());
}

void Simulation::addSubstance(const Substance & value)
{
	_substances.append(value);
}

void Simulation::setSubstanceMass(int index, float value)
{
	substance(index).mass = value;
	emit changedSubstanceMass(index, value);
}

void Simulation::setSubstanceColor(int index, const Color & value)
{
	substance(index).color = value;
	emit changedSubstanceColor(index, value);
}

void Simulation::setSubstancePressureFactor(int index, float value)
{
	substance(index).pressureFactor = value;
	emit changedSubstancePressureFactor(index, value);
}

void Simulation::setSubstanceRestDensity(int index, float value)
{
	substance(index).restDensity = value;
	emit changedSubstanceRestDensity(index, value);
}

void Simulation::setSubstanceViscosityFactor(int index, float value)
{
	substance(index).viscosityFactor = value;
	emit changedSubstanceViscosityFactor(index, value);
}

Substance & Simulation::substance(int index)
{
	// Crash if index is invalid
	// This is wanted for the moment
	return _substances[index];
}

